namespace 地图哇哇哇
{
       /// <summary>
    /// Double 的扩展
    /// </summary>
    //SOURCE: [https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Precision.cs](https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Precision.cs )
    //        [https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Precision.Equality.cs](https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Precision.Equality.cs )
    //        [http://referencesource.microsoft.com/#WindowsBase/Shared/MS/Internal/DoubleUtil.cs](http://referencesource.microsoft.com/#WindowsBase/Shared/MS/Internal/DoubleUtil.cs )
    //        [http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre](http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre )
    public static class DoubleExtensions
    {
        /// <summary>
        /// The smallest positive number that when SUBTRACTED from 1D yields a result different from 1D.
        ///
        /// This number has the following properties:
        ///     (1 - NegativeMachineEpsilon) &lt; 1 and
        ///     (1 + NegativeMachineEpsilon) == 1
        /// </summary>
        public static readonly double MeasuredNegativeMachineEpsilon = MeasureNegativeMachineEpsilon();

        /// <summary>
        /// The smallest positive number that when ADDED to 1D yields a result different from 1D.
        ///
        /// This number has the following properties:
        ///     (1 - PositiveDoublePrecision) &lt; 1 and
        ///     (1 + PositiveDoublePrecision) &gt; 1
        /// </summary>
        public static readonly double MeasuredPositiveMachineEpsilon = MeasurePositiveMachineEpsilon();


        /// <summary>
        /// The smallest positive number that when SUBTRACTED from 1D yields a result different from 1D.
        /// The value is derived from 2^(-53) = 1.1102230246251565e-16, where IEEE 754 binary64 &quot;double precision&quot; floating point numbers have a significand precision that utilize 53 bits.
        ///
        /// This number has the following properties:
        ///     (1 - NegativeMachineEpsilon) &lt; 1 and
        ///     (1 + NegativeMachineEpsilon) == 1
        /// </summary>
        public const double NegativeMachineEpsilon = 1.1102230246251565e-16D; //Math.Pow(2, -53);

        /// <summary>
        /// The smallest positive number that when ADDED to 1D yields a result different from 1D.
        /// The value is derived from 2 * 2^(-53) = 2.2204460492503131e-16, where IEEE 754 binary64 &quot;double precision&quot; floating point numbers have a significand precision that utilize 53 bits.
        ///
        /// This number has the following properties:
        ///     (1 - PositiveDoublePrecision) &lt; 1 and
        ///     (1 + PositiveDoublePrecision) &gt; 1
        /// </summary>
        public const double PositiveMachineEpsilon = 2D * NegativeMachineEpsilon;

        public static bool IsClose(this double value1, double value2,
            double maximumAbsoluteError = DefaultDoubleAccuracy)
        {
            if (double.IsInfinity(value1) || double.IsInfinity(value2))
            {
                return Equals(value1, value2);
            }

            if (double.IsNaN(value1) || double.IsNaN(value2))
            {
                return false;
            }

            var delta = value1 - value2;

            //return Math.Abs(delta) <= maximumAbsoluteError;

            if (delta > maximumAbsoluteError ||
                delta < -maximumAbsoluteError)
            {
                return false;
            }

            return true;
        }

        public static bool LessThan(this double value1, double value2)
        {
            return (value1 < value2) && !IsClose(value1, value2);
        }

        public static bool GreaterThan(this double value1, double value2)
        {
            return (value1 > value2) && !IsClose(value1, value2);
        }

        public static bool LessThanOrClose(this double value1, double value2)
        {
            return (value1 < value2) || IsClose(value1, value2);
        }

        public static bool GreaterThanOrClose(this double value1, double value2)
        {
            return (value1 > value2) || IsClose(value1, value2);
        }

        public static bool IsOne(this double value)
        {
            var delta = value - 1D;

            //return Math.Abs(delta) <= PositiveMachineEpsilon;

            if (delta > PositiveMachineEpsilon ||
                delta < -PositiveMachineEpsilon)
            {
                return false;
            }

            return true;
        }

        public static bool IsZero(this double value)
        {
            //return Math.Abs(value) <= PositiveMachineEpsilon;

            if (value > PositiveMachineEpsilon ||
                value < -PositiveMachineEpsilon)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 判断两个 <see cref="T:System.Double" /> 值是否近似相等。
        /// </summary>
        /// <param name="d1">值1。</param>
        /// <param name="d2">值2。</param>
        /// <param name="tolerance">近似容差。</param>
       
        public static bool NearlyEquals(double d1, double d2, double tolerance = 1E-05)
        {
            return IsClose(d1, d2, tolerance);
        }

        private static double MeasureNegativeMachineEpsilon()
        {
            var epsilon = 1D;

            do
            {
                var nextEpsilon = epsilon / 2D;

                if (NearlyEquals(1D - nextEpsilon, 1D)) //if nextEpsilon is too small
                {
                    return epsilon;
                }

                epsilon = nextEpsilon;
            } while (true);
        }

        private static double MeasurePositiveMachineEpsilon()
        {
            var epsilon = 1D;

            do
            {
                var nextEpsilon = epsilon / 2D;

                if (NearlyEquals((1D + nextEpsilon), 1D)) //if nextEpsilon is too small
                {
                    return epsilon;
                }

                epsilon = nextEpsilon;
            } while (true);
        }

        private const double DefaultDoubleAccuracy = NegativeMachineEpsilon * 10D;
    }
}